package com.powerwyx.shortlink.project.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.powerwyx.shortlink.project.common.biz.user.UserContext;
import com.powerwyx.shortlink.project.common.convention.errorcode.BaseErrorCode;
import com.powerwyx.shortlink.project.common.convention.exception.ClientException;
import com.powerwyx.shortlink.project.common.convention.exception.ServiceException;
import com.powerwyx.shortlink.project.mapper.*;
import com.powerwyx.shortlink.project.pojo.dto.ShortLinkGroupStatsAccessRecordDTO;
import com.powerwyx.shortlink.project.pojo.dto.ShortLinkGroupStatsDTO;
import com.powerwyx.shortlink.project.pojo.dto.ShortLinkStatsAccessRecordDTO;
import com.powerwyx.shortlink.project.pojo.dto.ShortLinkStatsDTO;
import com.powerwyx.shortlink.project.pojo.entity.*;
import com.powerwyx.shortlink.project.pojo.vo.*;
import com.powerwyx.shortlink.project.service.ShortLinkStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author 王艺锡
 * @version 1.0
 */
@Service
public class ShortLinkStatsServiceImpl implements ShortLinkStatsService {

    @Autowired
    private LinkAccessStatsMapper linkAccessStatsMapper;
    @Autowired
    private LinkLocalStatsMapper linkLocalStatsMapper;
    @Autowired
    private LinkAccessLogsMapper linkAccessLogsMapper;
    @Autowired
    private LinkBrowserStatsMapper linkBrowserStatsMapper;
    @Autowired
    private LinkOsStatsMapper linkOsStatsMapper;
    @Autowired
    private LinkDeviceStatsMapper linkDeviceStatsMapper;
    @Autowired
    private LinkNetworkStatsMapper linkNetworkStatsMapper;
    @Autowired
    private GroupMapper groupMapper;




    public void checkGroupBelongToUser(String gid) throws ServiceException {
        String username = Optional.ofNullable(UserContext.getUsername())
                .orElseThrow(() -> new ServiceException("用户未登录"));
        LambdaQueryWrapper<Group> queryWrapper = Wrappers.lambdaQuery(Group.class)
                .eq(Group::getGid, gid)
                .eq(Group::getUsername, username);
        List<Group> GroupList = groupMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(GroupList)) {
            throw new ServiceException("用户信息与分组标识不匹配");
        }
    }

    @Override
    public ShortLinkStatsVO oneShortLinkStats(ShortLinkStatsDTO shortLinkStatsDTO) {
        checkGroupBelongToUser(shortLinkStatsDTO.getGid());
        List<LinkAccessStats> listStatsByShortLink = linkAccessStatsMapper.listStatsByShortLink(shortLinkStatsDTO);
        if (CollUtil.isEmpty(listStatsByShortLink)) {
            return null;
        }
        // 基础访问数据
        LinkAccessStats pvUvUipStatsByShortLink = linkAccessLogsMapper.findPvUvUipStatsByShortLink(shortLinkStatsDTO);
        // 基础访问详情
        List<ShortLinkStatsAccessDailyVO> daily = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(shortLinkStatsDTO.getStartDate()),
                        DateUtil.parse(shortLinkStatsDTO.getEndDate()), DateField.DAY_OF_MONTH).stream()
                .map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> listStatsByShortLink.stream()
                .filter(item -> Objects.equals(each, DateUtil.formatDate(item.getDate())))
                .findFirst()
                .ifPresentOrElse(item -> {
                    ShortLinkStatsAccessDailyVO accessDailyRespDTO = ShortLinkStatsAccessDailyVO.builder()
                            .date(each)
                            .pv(item.getPv())
                            .uv(item.getUv())
                            .uip(item.getUip())
                            .build();
                    daily.add(accessDailyRespDTO);
                }, () -> {
                    ShortLinkStatsAccessDailyVO accessDailyRespDTO = ShortLinkStatsAccessDailyVO.builder()
                            .date(each)
                            .pv(0)
                            .uv(0)
                            .uip(0)
                            .build();
                    daily.add(accessDailyRespDTO);
                }));
        // 地区访问详情（仅国内）
        List<ShortLinkStatsLocalCNVO> localCnStats = new ArrayList<>();
        List<LinkLocalStats> listedLocaleByShortLink = linkLocalStatsMapper.listLocalByShortLink(shortLinkStatsDTO);
        int localeCnSum = listedLocaleByShortLink.stream()
                .mapToInt(LinkLocalStats::getCnt)
                .sum();
        listedLocaleByShortLink.forEach(each -> {
            double ratio = (double) each.getCnt() / localeCnSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsLocalCNVO localeCNRespDTO = ShortLinkStatsLocalCNVO.builder()
                    .cnt(each.getCnt())
                    .locale(each.getProvince())
                    .ratio(actualRatio)
                    .build();
            localCnStats.add(localeCNRespDTO);
        });
        // 小时访问详情
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStats> listHourStatsByShortLink = linkAccessStatsMapper.listHourStatsByShortLink(shortLinkStatsDTO);
        for (int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
            int hourCnt = listHourStatsByShortLink.stream()
                    .filter(each -> Objects.equals(each.getHour(), hour.get()))
                    .findFirst()
                    .map(LinkAccessStats::getPv)
                    .orElse(0);
            hourStats.add(hourCnt);
        }
        // 高频访问IP详情
        List<ShortLinkStatsTopIpVO> topIpStats = new ArrayList<>();
        List<HashMap<String, Object>> listTopIpByShortLink = linkAccessLogsMapper.listTopIpByShortLink(shortLinkStatsDTO);
        listTopIpByShortLink.forEach(each -> {
            ShortLinkStatsTopIpVO statsTopIpRespDTO = ShortLinkStatsTopIpVO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpStats.add(statsTopIpRespDTO);
        });
        // 一周访问详情
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStats> listWeekdayStatsByShortLink = linkAccessStatsMapper.listWeekdayStatsByShortLink(shortLinkStatsDTO);
        for (int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByShortLink.stream()
                    .filter(each -> Objects.equals(each.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStats::getPv)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        // 浏览器访问详情
        List<ShortLinkStatsBrowserVO> browserStats = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByShortLink = linkBrowserStatsMapper.listBrowserStatsByShortLink(shortLinkStatsDTO);
        int browserSum = listBrowserStatsByShortLink.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listBrowserStatsByShortLink.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / browserSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsBrowserVO browserRespDTO = ShortLinkStatsBrowserVO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .browser(each.get("browser").toString())
                    .ratio(actualRatio)
                    .build();
            browserStats.add(browserRespDTO);
        });
        // 操作系统访问详情
        List<ShortLinkStatsOsVO> osStats = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByShortLink = linkOsStatsMapper.listOsStatsByShortLink(shortLinkStatsDTO);
        int osSum = listOsStatsByShortLink.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listOsStatsByShortLink.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / osSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsOsVO osRespDTO = ShortLinkStatsOsVO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .os(each.get("os").toString())
                    .ratio(actualRatio)
                    .build();
            osStats.add(osRespDTO);
        });
        // 访客访问类型详情
        List<ShortLinkStatsUvVO> uvTypeStats = new ArrayList<>();
        HashMap<String, Object> findUvTypeByShortLink = linkAccessLogsMapper.findUvTypeCntByShortLink(shortLinkStatsDTO);
        int oldUserCnt = Integer.parseInt(
                Optional.ofNullable(findUvTypeByShortLink)
                        .map(each -> each.get("oldUserCnt"))
                        .map(Object::toString)
                        .orElse("0")
        );
        int newUserCnt = Integer.parseInt(
                Optional.ofNullable(findUvTypeByShortLink)
                        .map(each -> each.get("newUserCnt"))
                        .map(Object::toString)
                        .orElse("0")
        );
        int uvSum = oldUserCnt + newUserCnt;
        double oldRatio = (double) oldUserCnt / uvSum;
        double actualOldRatio = Math.round(oldRatio * 100.0) / 100.0;
        double newRatio = (double) newUserCnt / uvSum;
        double actualNewRatio = Math.round(newRatio * 100.0) / 100.0;
        ShortLinkStatsUvVO newUvRespDTO = ShortLinkStatsUvVO.builder()
                .uvType("newUser")
                .cnt(newUserCnt)
                .ratio(actualNewRatio)
                .build();
        uvTypeStats.add(newUvRespDTO);
        ShortLinkStatsUvVO oldUvRespDTO = ShortLinkStatsUvVO.builder()
                .uvType("oldUser")
                .cnt(oldUserCnt)
                .ratio(actualOldRatio)
                .build();
        uvTypeStats.add(oldUvRespDTO);
        // 访问设备类型详情
        List<ShortLinkStatsDeviceVO> deviceStats = new ArrayList<>();
        List<LinkDeviceStats> listDeviceStatsByShortLink = linkDeviceStatsMapper.listDeviceStatsByShortLink(shortLinkStatsDTO);
        int deviceSum = listDeviceStatsByShortLink.stream()
                .mapToInt(LinkDeviceStats::getCnt)
                .sum();
        listDeviceStatsByShortLink.forEach(each -> {
            double ratio = (double) each.getCnt() / deviceSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsDeviceVO deviceRespDTO = ShortLinkStatsDeviceVO.builder()
                    .cnt(each.getCnt())
                    .device(each.getDevice())
                    .ratio(actualRatio)
                    .build();
            deviceStats.add(deviceRespDTO);
        });
        // 访问网络类型详情
        List<ShortLinkStatsNetworkVO> networkStats = new ArrayList<>();
        List<LinkNetworkStats> listNetworkStatsByShortLink = linkNetworkStatsMapper.listNetworkStatsByShortLink(shortLinkStatsDTO);
        int networkSum = listNetworkStatsByShortLink.stream()
                .mapToInt(LinkNetworkStats::getCnt)
                .sum();
        listNetworkStatsByShortLink.forEach(each -> {
            double ratio = (double) each.getCnt() / networkSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsNetworkVO networkRespDTO = ShortLinkStatsNetworkVO.builder()
                    .cnt(each.getCnt())
                    .network(each.getNetwork())
                    .ratio(actualRatio)
                    .build();
            networkStats.add(networkRespDTO);
        });
        return ShortLinkStatsVO.builder()
                .pv(pvUvUipStatsByShortLink.getPv())
                .uv(pvUvUipStatsByShortLink.getUv())
                .uip(pvUvUipStatsByShortLink.getUip())
                .daily(daily)
                .localCnStats(localCnStats)
                .hourStats(hourStats)
                .topIpStats(topIpStats)
                .weekdayStats(weekdayStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .uvTypeStats(uvTypeStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();
    }



    /**
     *获取分组短链接监控数据
     * @param shortLinkGroupStatsDTO
     * @return
     */
    @Override
    public ShortLinkStatsVO groupShortLinkStats(ShortLinkGroupStatsDTO shortLinkGroupStatsDTO) {
        checkGroupBelongToUser(shortLinkGroupStatsDTO.getGid());
        List<LinkAccessStats> listStatsByGroup = linkAccessStatsMapper.listStatsByGroup(shortLinkGroupStatsDTO);
        if (CollUtil.isEmpty(listStatsByGroup)) {
            return null;
        }
        // 基础访问数据
        LinkAccessStats pvUvUipStatsByGroup = linkAccessLogsMapper.findPvUvUipStatsByGroup(shortLinkGroupStatsDTO);
        // 基础访问详情
        List<ShortLinkStatsAccessDailyVO> daily = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(shortLinkGroupStatsDTO.getStartDate()), DateUtil.parse(shortLinkGroupStatsDTO.getEndDate()), DateField.DAY_OF_MONTH).stream()
                .map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> listStatsByGroup.stream()
                .filter(item -> Objects.equals(each, DateUtil.formatDate(item.getDate())))
                .findFirst()
                .ifPresentOrElse(item -> {
                    ShortLinkStatsAccessDailyVO accessDailyRespDTO = ShortLinkStatsAccessDailyVO.builder()
                            .date(each)
                            .pv(item.getPv())
                            .uv(item.getUv())
                            .uip(item.getUip())
                            .build();
                    daily.add(accessDailyRespDTO);
                }, () -> {
                    ShortLinkStatsAccessDailyVO accessDailyRespDTO = ShortLinkStatsAccessDailyVO.builder()
                            .date(each)
                            .pv(0)
                            .uv(0)
                            .uip(0)
                            .build();
                    daily.add(accessDailyRespDTO);
                }));
        // 地区访问详情（仅国内）
        List<ShortLinkStatsLocalCNVO> localeCnStats = new ArrayList<>();
        List<LinkLocalStats> listedLocaleByGroup = linkLocalStatsMapper.listLocaleByGroup(shortLinkGroupStatsDTO);
        int localeCnSum = listedLocaleByGroup.stream()
                .mapToInt(LinkLocalStats::getCnt)
                .sum();
        listedLocaleByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / localeCnSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsLocalCNVO localeCNRespDTO = ShortLinkStatsLocalCNVO.builder()
                    .cnt(each.getCnt())
                    .locale(each.getProvince())
                    .ratio(actualRatio)
                    .build();
            localeCnStats.add(localeCNRespDTO);
        });
        // 小时访问详情
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStats> listHourStatsByGroup = linkAccessStatsMapper.listHourStatsByGroup(shortLinkGroupStatsDTO);
        for (int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
            int hourCnt = listHourStatsByGroup.stream()
                    .filter(each -> Objects.equals(each.getHour(), hour.get()))
                    .findFirst()
                    .map(LinkAccessStats::getPv)
                    .orElse(0);
            hourStats.add(hourCnt);
        }
        // 高频访问IP详情
        List<ShortLinkStatsTopIpVO> topIpStats = new ArrayList<>();
        List<HashMap<String, Object>> listTopIpByGroup = linkAccessLogsMapper.listTopIpByGroup(shortLinkGroupStatsDTO);
        listTopIpByGroup.forEach(each -> {
            ShortLinkStatsTopIpVO statsTopIpRespDTO = ShortLinkStatsTopIpVO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpStats.add(statsTopIpRespDTO);
        });
        // 一周访问详情
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStats> listWeekdayStatsByGroup = linkAccessStatsMapper.listWeekdayStatsByGroup(shortLinkGroupStatsDTO);
        for (int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByGroup.stream()
                    .filter(each -> Objects.equals(each.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStats::getPv)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        // 浏览器访问详情
        List<ShortLinkStatsBrowserVO> browserStats = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByGroup = linkBrowserStatsMapper.listBrowserStatsByGroup(shortLinkGroupStatsDTO);
        int browserSum = listBrowserStatsByGroup.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listBrowserStatsByGroup.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / browserSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsBrowserVO browserRespDTO = ShortLinkStatsBrowserVO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .browser(each.get("browser").toString())
                    .ratio(actualRatio)
                    .build();
            browserStats.add(browserRespDTO);
        });
        // 操作系统访问详情
        List<ShortLinkStatsOsVO> osStats = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByGroup = linkOsStatsMapper.listOsStatsByGroup(shortLinkGroupStatsDTO);
        int osSum = listOsStatsByGroup.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listOsStatsByGroup.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / osSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsOsVO osRespDTO = ShortLinkStatsOsVO.builder()
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .os(each.get("os").toString())
                    .ratio(actualRatio)
                    .build();
            osStats.add(osRespDTO);
        });
        // 访问设备类型详情
        List<ShortLinkStatsDeviceVO> deviceStats = new ArrayList<>();
        List<LinkDeviceStats> listDeviceStatsByGroup = linkDeviceStatsMapper.listDeviceStatsByGroup(shortLinkGroupStatsDTO);
        int deviceSum = listDeviceStatsByGroup.stream()
                .mapToInt(LinkDeviceStats::getCnt)
                .sum();
        listDeviceStatsByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / deviceSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsDeviceVO deviceRespDTO = ShortLinkStatsDeviceVO.builder()
                    .cnt(each.getCnt())
                    .device(each.getDevice())
                    .ratio(actualRatio)
                    .build();
            deviceStats.add(deviceRespDTO);
        });
        // 访问网络类型详情
        List<ShortLinkStatsNetworkVO> networkStats = new ArrayList<>();
        List<LinkNetworkStats> listNetworkStatsByGroup = linkNetworkStatsMapper.listNetworkStatsByGroup(shortLinkGroupStatsDTO);
        int networkSum = listNetworkStatsByGroup.stream()
                .mapToInt(LinkNetworkStats::getCnt)
                .sum();
        listNetworkStatsByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / networkSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsNetworkVO networkRespDTO = ShortLinkStatsNetworkVO.builder()
                    .cnt(each.getCnt())
                    .network(each.getNetwork())
                    .ratio(actualRatio)
                    .build();
            networkStats.add(networkRespDTO);
        });
        return ShortLinkStatsVO.builder()
                .pv(pvUvUipStatsByGroup.getPv())
                .uv(pvUvUipStatsByGroup.getUv())
                .uip(pvUvUipStatsByGroup.getUip())
                .daily(daily)
                .localCnStats(localeCnStats)
                .hourStats(hourStats)
                .topIpStats(topIpStats)
                .weekdayStats(weekdayStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();

    }




    /**
     * 访问单个短链接指定时间内访问记录监控数据
     *
     * @param shortLinkStatsAccessRecordDTO
     * @return
     */
    @Override
    public IPage<ShortLinkStatsAccessRecordVO> shortLinkStatsAccessRecord(ShortLinkStatsAccessRecordDTO shortLinkStatsAccessRecordDTO) {
        if (shortLinkStatsAccessRecordDTO == null) {
            throw new ClientException(BaseErrorCode.PARAMS_ERROR);
        }
        checkGroupBelongToUser(shortLinkStatsAccessRecordDTO.getGid());
        IPage<LinkAccessLogs> linkAccessLogsIPage = linkAccessLogsMapper.selectPage(shortLinkStatsAccessRecordDTO, new QueryWrapper<LinkAccessLogs>()
                .eq("gid", shortLinkStatsAccessRecordDTO.getGid())
                .eq("full_short_url", shortLinkStatsAccessRecordDTO.getFullShortUrl())
                .eq("del_flag", 0)
                .between("create_time", shortLinkStatsAccessRecordDTO.getStartDate(), shortLinkStatsAccessRecordDTO.getEndDate())
                .orderByDesc("create_time")
        );

        IPage<ShortLinkStatsAccessRecordVO> actualResult = linkAccessLogsIPage
                .convert(each -> BeanUtil.copyProperties(each, ShortLinkStatsAccessRecordVO.class));
        //取出用户
        List<String> userAccessLogsList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordVO::getUser)
                .toList();

        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectUvTypeByUsers(
                shortLinkStatsAccessRecordDTO.getGid(),
                shortLinkStatsAccessRecordDTO.getFullShortUrl(),
                shortLinkStatsAccessRecordDTO.getStartDate(),
                shortLinkStatsAccessRecordDTO.getEndDate(),
                userAccessLogsList
        );

        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream()
                    .filter(item -> Objects.equals(item.get("user"), each.getUser()))
                    .findFirst()
                    .map(item -> item.get("uvType"))
                    .map(Object::toString)
                    .orElse("旧访客");
            each.setUvType(uvType);
        });

        return actualResult;
    }


    /**
     *访问分组短链接指定时间内访问记录监控数据
     * @param shortLinkGroupStatsAccessRecordDTO
     * @return
     */
    @Override
    public IPage<ShortLinkStatsAccessRecordVO> groupShortLinkStatsAccessRecord(ShortLinkGroupStatsAccessRecordDTO shortLinkGroupStatsAccessRecordDTO) {
        if (shortLinkGroupStatsAccessRecordDTO == null) {
            throw new ClientException(BaseErrorCode.PARAMS_ERROR);
        }
        checkGroupBelongToUser(shortLinkGroupStatsAccessRecordDTO.getGid());
        IPage<LinkAccessLogs> linkAccessLogsIPage = linkAccessLogsMapper.selectPage(shortLinkGroupStatsAccessRecordDTO, new QueryWrapper<LinkAccessLogs>()
                .eq("gid", shortLinkGroupStatsAccessRecordDTO.getGid())
                .eq("del_flag", 0)
                .between("create_time", shortLinkGroupStatsAccessRecordDTO.getStartDate(), shortLinkGroupStatsAccessRecordDTO.getEndDate())
                .orderByDesc("create_time")
        );

        IPage<ShortLinkStatsAccessRecordVO> actualResult = linkAccessLogsIPage
                .convert(each -> BeanUtil.copyProperties(each, ShortLinkStatsAccessRecordVO.class));
        //取出用户
        List<String> userAccessLogsList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordVO::getUser)
                .toList();

        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectGroupUvTypeByUsers(
                shortLinkGroupStatsAccessRecordDTO.getGid(),
                shortLinkGroupStatsAccessRecordDTO.getStartDate(),
                shortLinkGroupStatsAccessRecordDTO.getEndDate(),
                userAccessLogsList
        );

        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream()
                    .filter(item -> Objects.equals(item.get("user"), each.getUser()))
                    .findFirst()
                    .map(item -> item.get("uvType"))
                    .map(Object::toString)
                    .orElse("旧访客");
            each.setUvType(uvType);
        });

        return actualResult;
    }


}
